﻿using System;
using System.Net;
using System.Threading;
using System.Diagnostics;
using System.ServiceModel;
using System.ComponentModel;
using System.ServiceProcess;
using System.Collections.Generic;
using System.Configuration.Install;
using System.ServiceModel.Discovery;
using System.ServiceModel.Description;

using Builderator.Core;
using Builderator.Core.Plugin;
using Builderator.Core.Trigger;
using Builderator.Core.Interfaces;

namespace Builderator.Service
{
    public class BuilderatorService : ServiceBase
    {
        static void Main()
        {
            ServiceBase.Run(new BuilderatorService());
        }

        private ServiceHost serviceHost;
        private BuilderatorEngine builderatorEngine;
        private Dictionary<Guid, Thread> triggerDictionary;

        public BuilderatorService()
        {
            this.ServiceName = "BuilderatorService";
        }

        protected override void OnStart(string[] args)
        {
            try
            {
                this.triggerDictionary = new Dictionary<Guid, Thread>();

                this.builderatorEngine = new BuilderatorEngine();

                this.builderatorEngine.ActionUpdatedEvent += new ActionUpdatedDelegate(builderatorEngine_ActionUpdatedEvent);

                this.RegisterTriggers();

                this.serviceHost = new ServiceHost(this.builderatorEngine, new Uri(string.Format("http://{0}:9999", Dns.GetHostName())));

                ServiceMetadataBehavior smb = new ServiceMetadataBehavior();

                smb.HttpGetEnabled = true;

                this.serviceHost.Description.Behaviors.Add(smb);

                ServiceDiscoveryBehavior sdb = new ServiceDiscoveryBehavior();

                sdb.AnnouncementEndpoints.Add(new UdpAnnouncementEndpoint());

                this.serviceHost.Description.Behaviors.Add(sdb);

                this.serviceHost.AddServiceEndpoint(typeof(IBuilderatorEngine), new WSDualHttpBinding(), "Builderator");

                this.serviceHost.AddServiceEndpoint(new UdpDiscoveryEndpoint());

                this.serviceHost.Closed += new EventHandler(serviceHost_Closed);

                this.serviceHost.Faulted += new EventHandler(serviceHost_Faulted);

                this.serviceHost.UnknownMessageReceived += new EventHandler<UnknownMessageReceivedEventArgs>(serviceHost_UnknownMessageReceived);

                this.serviceHost.Open();
            }
            catch (Exception e)
            {
                this.serviceHost.Abort();

                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);
            }
        }

        void serviceHost_UnknownMessageReceived(object sender, UnknownMessageReceivedEventArgs e)
        {
            this.DebugMessage("Service unknown message received");
        }

        void serviceHost_Faulted(object sender, EventArgs e)
        {
            this.DebugMessage("Service has faulted");
        }

        void serviceHost_Closed(object sender, EventArgs e)
        {
            this.DebugMessage("Service has closed");
        }

        private void builderatorEngine_ActionUpdatedEvent(IAction action, ActionUpdatedEnum type)
        {
            try
            {
                switch (type)
                {
                    case ActionUpdatedEnum.Add:
                        {
                            Thread thread = new Thread(new ThreadStart(delegate
                                {
                                    ((ITrigger)action).Start();
                                }));

                            thread.Start();

                            this.triggerDictionary.Add(action.Identifier, thread);
                        }
                        break;
                    case ActionUpdatedEnum.Remove:
                        {
                            this.triggerDictionary[action.Identifier].Abort();

                            this.triggerDictionary.Remove(action.Identifier);
                        }
                        break;
                    case ActionUpdatedEnum.Update:
                        {
                            this.triggerDictionary[action.Identifier].Abort();

                            Thread thread = new Thread(new ThreadStart(delegate
                            {
                                ((ITrigger)action).Start();
                            }));

                            thread.Start();

                            this.triggerDictionary[action.Identifier] = thread;
                        }
                        break;
                }
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);
            }
        }

        private void RegisterTriggers()
        {
            try
            {
                foreach (ITrigger trigger in this.builderatorEngine.GetTriggers())
                {
                    this.DebugMessage(string.Format("Registering trigger {0}", trigger.Name));

                    trigger.TriggerEvent += new TriggerDelegate(trigger_TriggerEvent);

                    trigger.DebugMessageEvent += new DebugMessageDelegate(this.builderatorEngine.DebugMessage);

                    if (trigger is IDependency)
                    {
                        this.DebugMessage(string.Format("Trigger is IDependency"));

                        ((IDependency)trigger).SourceActions = this.builderatorEngine.GetAssociatedActionsBySubType(trigger.Associated, typeof(ISource));
                    }

                    Thread thread = new Thread(new ThreadStart(delegate
                        {
                            trigger.Start();
                        }));

                    thread.Start();

                    this.triggerDictionary.Add(trigger.Identifier, thread);
                }
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);
            }
        }

        private void trigger_TriggerEvent(Guid buildIdentifier)
        {
            try
            {
                this.builderatorEngine.RunBuild(buildIdentifier);
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);
            }
        }

        protected override void OnStop()
        {
            try
            {
                this.serviceHost.Close();

                foreach (KeyValuePair<Guid, Thread> item in this.triggerDictionary)
                {
                    item.Value.Abort();
                }
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);
            }
        }

        private void DebugMessage(string message)
        {
            Trace.WriteLine(message);

            Debug.WriteLine(message);
        }
    }

    [RunInstaller(true)]
    public class BuilderatorServiceInstaller : Installer
    {
        private ServiceInstaller installer;
        private ServiceProcessInstaller process;

        public BuilderatorServiceInstaller()
        {
            this.installer = new ServiceInstaller();
            this.process = new ServiceProcessInstaller();

            this.process.Account = ServiceAccount.LocalSystem;

            installer.DisplayName = "Builderator";
            installer.StartType = ServiceStartMode.Automatic;
            installer.ServiceName = "BuilderatorService";

            this.Installers.Add(process);
            this.Installers.Add(installer);
        }
    }
}